//#include "S3C2416.h"
#define ELFIN_HSMMC_BASE	0x4a800000

#define HM_SYSAD	(ELFIN_HSMMC_BASE+0x00)
#define HM_BLKSIZE	(ELFIN_HSMMC_BASE+0x04)
#define HM_BLKCNT	(ELFIN_HSMMC_BASE+0x06)
#define HM_ARGUMENT	(ELFIN_HSMMC_BASE+0x08)
#define HM_TRNMOD	(ELFIN_HSMMC_BASE+0x0c)
#define HM_CMDREG	(ELFIN_HSMMC_BASE+0x0e)
#define HM_RSPREG0	(ELFIN_HSMMC_BASE+0x10)
#define HM_RSPREG1	(ELFIN_HSMMC_BASE+0x14)
#define HM_RSPREG2	(ELFIN_HSMMC_BASE+0x18)
#define HM_RSPREG3	(ELFIN_HSMMC_BASE+0x1c)
#define HM_BDATA	(ELFIN_HSMMC_BASE+0x20)
#define HM_PRNSTS	(ELFIN_HSMMC_BASE+0x24)
#define HM_HOSTCTL	(ELFIN_HSMMC_BASE+0x28)
#define HM_PWRCON	(ELFIN_HSMMC_BASE+0x29)
#define HM_BLKGAP	(ELFIN_HSMMC_BASE+0x2a)
#define HM_WAKCON	(ELFIN_HSMMC_BASE+0x2b)
#define HM_CLKCON	(ELFIN_HSMMC_BASE+0x2c)
#define HM_TIMEOUTCON	(ELFIN_HSMMC_BASE+0x2e)
#define HM_SWRST	(ELFIN_HSMMC_BASE+0x2f)
#define HM_NORINTSTS	(ELFIN_HSMMC_BASE+0x30)
#define HM_ERRINTSTS	(ELFIN_HSMMC_BASE+0x32)
#define HM_NORINTSTSEN	(ELFIN_HSMMC_BASE+0x34)
#define HM_ERRINTSTSEN	(ELFIN_HSMMC_BASE+0x36)
#define HM_NORINTSIGEN	(ELFIN_HSMMC_BASE+0x38)
#define HM_ERRINTSIGEN	(ELFIN_HSMMC_BASE+0x3a)
#define HM_ACMD12ERRSTS	(ELFIN_HSMMC_BASE+0x3c)
#define HM_CAPAREG	(ELFIN_HSMMC_BASE+0x40)
#define HM_MAXCURR	(ELFIN_HSMMC_BASE+0x48)
#define HM_CONTROL2	(ELFIN_HSMMC_BASE+0x80)
#define HM_CONTROL3	(ELFIN_HSMMC_BASE+0x84)
#define HM_CONTROL4	(ELFIN_HSMMC_BASE+0x8C)
#define HM_HCVER	(ELFIN_HSMMC_BASE+0xfe)

#define GPECON		(0x56000040)
#define GPEDAT		(0x56000044)
#define GPEPU		(0x56000048)

#define GPFCON		(0x56000050)
#define GPFDAT		(0x56000054)
#define GPFPU		(0x56000058)

#define GPGCON		(0x56000060)
#define GPGDAT		(0x56000064)
#define GPGPU		(0x56000068)

#define GPHCON		(0x56000070)
#define GPHDAT		(0x56000074)
#define GPHPU		(0x56000078)

#define GPJCON		(0x560000d0)
#define GPJDAT		(0x560000d4)
#define GPJPU		(0x560000d8)

#define GPKCON		(0x560000e0)
#define GPKDAT		(0x560000e4)
#define GPKPU		(0x560000e8)

#define GPLCON		(0x560000f0)
#define GPLDAT		(0x560000f4)
#define GPLUDP		(0x560000f8)


typedef unsigned char		unchar;
typedef unsigned short		ushort;
typedef unsigned int		uint;
typedef unsigned long		ulong;

typedef unsigned char		uchar;
typedef volatile unsigned long	vu_long;
typedef volatile unsigned short vu_short;
typedef volatile unsigned char	vu_char;


typedef signed char s8;
typedef unsigned char u8;

typedef signed short s16;
typedef unsigned short u16;

typedef signed int s32;
typedef unsigned int u32;

typedef signed long long s64;
typedef unsigned long long u64;

#define __iomem
#define __force 


#define HSMMC_CHANNEL 1
#define CONFIG_SUPPORT_MMC_PLUS
#define HCLK_OPERATION
//#define DEBUG_HSMMC

#ifdef DEBUG_HSMMC
#define dbg(x...)       printf(x)
#else
#define dbg(x...)       do { } while (0)
#endif

#include "kern/types.h"

#include "mmc.h"
#include "protocol.h"
#include "hs_mmc.h"
#include "io.h"
#include "stdio.h"

#define SDI_Tx_buffer_HSMMC		(0x51000000)
#define SDI_Rx_buffer_HSMMC		(0x51000000+(0x300000))
#define SDI_Compare_buffer_HSMMC	(0x51000000+(0x600000))

#define Card_OneBlockSize_ver1		512

/*  Global variables */
static uint *Tx_buffer_HSMMC;
static uint *Rx_buffer_HSMMC;
static uint *Compare_buffer_HSMMC;

static vu_long rd_cnt_HSMMC;
static vu_long wt_cnt_HSMMC;
static vu_long BlockNum_HSMMC = 0;

static vu_long WriteBlockCnt_INT = 0;
static vu_long ReadBlockCnt_INT = 0;
static vu_long WRITEINT_DONE = 0;
static vu_long READINT_DONE = 0;
static vu_long COMPARE_INT_DONE = 0;
static vu_long CompareCnt_INT = 0;
static vu_long BufferBoundary_INT_Cnt = 0;

static vu_long HS_DMA_END = 0;
static vu_long HS_CARD_DETECT = 0;

static uint ocr_check = 0;
static uint mmc_card = 0;
static uint rca = 0;
static uint mmc_spec, sd_spec;

static ulong HCLK;
static uint card_mid = 0;

/* extern variables */
uint movi_hc = 0;

/* extern functions */
extern ulong get_HCLK(void);

#define s3c_hsmmc_readl(x)	readl(x)
#define s3c_hsmmc_readw(x)	readw(x)
#define s3c_hsmmc_readb(x)	readb(x)

#define s3c_hsmmc_writel(v,x)	writel((v),(x))
#define s3c_hsmmc_writew(v,x)	writew((v),(x))
#define s3c_hsmmc_writeb(v,x)	writeb((v),(x))

static int wait_for_r_buf_ready (void)
{
	uint uLoop = 0;

	while (!(s3c_hsmmc_readw(HM_NORINTSTS) & 0x20)) {
		if (uLoop % 500000 == 0 && uLoop > 0) {
			return 0;
		}
		uLoop++;
	}
	return 1;
}

static int wait_for_cmd_done (void)
{
	uint i;
	ushort n_int, e_int;

	dbg("wait_for_cmd_done\r\n");
	for (i=0; i<0x20000000; i++) {
		n_int = s3c_hsmmc_readw(HM_NORINTSTS);
		dbg("  HM_NORINTSTS: %04x\r\n", n_int);
		if (n_int & 0x8000) break;
		if (n_int & 0x0001) return 0;
	}

	e_int = s3c_hsmmc_readw(HM_ERRINTSTS);
	s3c_hsmmc_writew(e_int, HM_ERRINTSTS);
	s3c_hsmmc_writew(n_int, HM_NORINTSTS);
	dbg("cmd error1: 0x%04x, HM_NORINTSTS: 0x%04x\r\n", e_int, n_int);
	return -1;
}

/* XXX: must modify algorithm. it has bugs. by scsuh */
static int wait_for_data_done (void)
{
	while (!(s3c_hsmmc_readw(HM_NORINTSTS) & 0x2))
		return 1;
}

static void ClearCommandCompleteStatus(void)
{
	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 0), HM_NORINTSTS);
	while (s3c_hsmmc_readw(HM_NORINTSTS) & 0x1) {
		s3c_hsmmc_writew(1 << 0, HM_NORINTSTS);
	}
}

static void ClearTransferCompleteStatus(void)
{
	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 1), HM_NORINTSTS);
	while (s3c_hsmmc_readw(HM_NORINTSTS) & 0x2) {
		s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 1), HM_NORINTSTS);
	}
}

static void ClearBufferReadReadyStatus(void)
{
	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 5), HM_NORINTSTS);
	while (s3c_hsmmc_readw(HM_NORINTSTS) & 0x20)
		s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 5), HM_NORINTSTS);
}

#if 0
static void HS_MMC_CardDetect(void)
{
	rGPJCON = (rGPJCON & ~(0x3 << 28)) | (1 << 29);
	//rHM_CONTROL2 |= (1<<13)|(1<<12);/* Card Detect using a DAT[3] signal */

	rHM_NORINTSTSEN |= (1 << 7) | (1 << 6);
	rHM_NORINTSIGEN |= (1 << 7) | (1 << 6);

	if (rHM_PRNSTS & (1 << 16)) {
		HS_CARD_DETECT = 1;
		printf("\r\nCard is Inserted\r\n");
	}

	while (!HS_CARD_DETECT) {
		printf("\r\nInsert a Card\r\n");
		HS_CARD_DETECT = 0;
		do {
			printf(".");
			udelay(500000);
		} while (HS_CARD_DETECT == 0);
	}

	HS_CARD_DETECT = 0;
	rHM_CONTROL2 &= ~(1 << 13);
}
#endif

static void card_irq_enable(ushort temp)
{
	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSTSEN) & 0xFEFF) | (temp << 8), HM_NORINTSTSEN);
}

void hsmmc_reset (void)
{
	s3c_hsmmc_writeb(0x3, HM_SWRST);
}

void hsmmc_set_gpio (void)
{
	u32 reg;

#if (HSMMC_CHANNEL == 0)
	reg = readl(GPECON) & 0xfffff81f;
	writel(reg | 0x2aa800, GPECON);

	reg = readl(GPEUDP) & 0xfffff81f;
	writel(reg, GPEUDP);
#elif (HSMMC_CHANNEL == 1)
	reg = readl(GPLCON) & ~(0xffff);
	writel(reg | 0xaaaaa, GPLCON);
	reg = readl(GPLUDP) & 0xfff00000;
	writel(reg, GPLUDP);

	reg = readl(GPJCON) & ~(0x3f << 26);
	writel(reg | (0x2a<<26), GPJCON);
	reg = readl(GPJDAT);
	writel(reg | (1<<15), GPJDAT);
#else
	printf("### HS-MMC channel is not defined!\r\n");
#endif
}

static void set_transfer_mode_register (uint MultiBlk, uint DataDirection, uint AutoCmd12En, uint BlockCntEn, uint DmaEn)
{
	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_TRNMOD) & ~(0xffff)) | (MultiBlk << 5)
		| (DataDirection << 4) | (AutoCmd12En << 2)
		| (BlockCntEn << 1) | (DmaEn << 0), HM_TRNMOD);
	dbg("\r\nHM_TRNMOD = 0x%04x\r\n", HM_TRNMOD);
}

static void set_arg_register (uint arg)
{
	s3c_hsmmc_writel(arg, HM_ARGUMENT);
}

static void set_blkcnt_register(ushort uBlkCnt)
{
	s3c_hsmmc_writew(uBlkCnt, HM_BLKCNT);
}

static void SetSystemAddressReg(uint SysAddr)
{
	s3c_hsmmc_writel(SysAddr, HM_SYSAD);
}

static void set_blksize_register(ushort uDmaBufBoundary, ushort uBlkSize)
{
	s3c_hsmmc_writew((uDmaBufBoundary << 12) | (uBlkSize), HM_BLKSIZE);
}

static void ClearErrInterruptStatus(void)
{
	while (s3c_hsmmc_readw(HM_NORINTSTS) & (0x1 << 15)) {
		s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS), HM_NORINTSTS);
		s3c_hsmmc_writew(s3c_hsmmc_readw(HM_ERRINTSTS), HM_ERRINTSTS);
	}
}

static void InterruptEnable(ushort NormalIntEn, ushort ErrorIntEn)
{
	ClearErrInterruptStatus();
	s3c_hsmmc_writew(NormalIntEn, HM_NORINTSTSEN);
	s3c_hsmmc_writew(ErrorIntEn, HM_ERRINTSTSEN);
}

static void hsmmc_clock_onoff (int on)
{
	u16 reg16;

	if (on == 0) {
		reg16 = s3c_hsmmc_readw(HM_CLKCON) & ~(0x1<<2);
		s3c_hsmmc_writew(reg16, HM_CLKCON);
	} else {
		reg16 = s3c_hsmmc_readw(HM_CLKCON);
		s3c_hsmmc_writew(reg16 | (0x1<<2), HM_CLKCON);

		while (1) {
			reg16 = s3c_hsmmc_readw(HM_CLKCON);
			if (reg16 & (0x1<<3))	/*  SD_CLKSRC is Stable */
				break;
		}
	}
}

static void set_clock (uint clksrc, uint div)
{
	u16 reg16;
	uint i;

	s3c_hsmmc_writel(0xC0000100 | (clksrc << 4), HM_CONTROL2);	// feedback control off
	s3c_hsmmc_writel((0<<31) | (0<<23) | (0<<15) | (0<<7) , HM_CONTROL3);
	s3c_hsmmc_writel(0x3 << 16, HM_CONTROL4);

	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_CLKCON) & ~(0xff << 8))|(0x80<<8)|(1<<0), HM_CLKCON);

	/* SDCLK Value Setting + Internal Clock Enable */
	s3c_hsmmc_writew(((div<<8) | 0x1), HM_CLKCON);

	/* CheckInternalClockStable */
	for (i=0; i<0x10000; i++) {
		reg16 = s3c_hsmmc_readw(HM_CLKCON);
		if (reg16 & 0x2)
			break;
	}
	if (i == 0x10000)
		printf("internal clock stabilization failed\r\n");
    
	dbg("HM_CONTROL2(0x80) = 0x%08x\r\n", s3c_hsmmc_readl(HM_CONTROL2));
	dbg("HM_CONTROL3(0x84) = 0x%08x\r\n", s3c_hsmmc_readl(HM_CONTROL3));
	dbg("HM_CLKCON  (0x2c) = 0x%04x\r\n", s3c_hsmmc_readw(HM_CLKCON));

	hsmmc_clock_onoff(1);
}

static void set_cmd_register (ushort cmd, uint data, uint flags)
{
	ushort val = (cmd << 8);

	if (cmd == 12)
		val |= (3 << 6);

	if (flags & MMC_RSP_136)	/* Long RSP */
		val |= 0x01;
	else if (flags & MMC_RSP_BUSY)	/* R1B */
		val |= 0x03;
	else if (flags & MMC_RSP_PRESENT)	/* Normal RSP */
		val |= 0x02;

	if (flags & MMC_RSP_OPCODE)
		val |= (1<<4);

	if (flags & MMC_RSP_CRC)
		val |= (1<<3);

	if (data)
		val |= (1<<5);

	dbg("cmdreg =  0x%04x\r\n", val);
	s3c_hsmmc_writew(val, HM_CMDREG);
}

static int issue_command (ushort cmd, uint arg, uint data, uint flags)
{
	int i;

	dbg("### issue_command: %d, %08x, %d, %08x\r\n", cmd, arg, data, flags);
	/* Check CommandInhibit_CMD */
	for (i=0; i<0x1000000; i++) {
		if (!(s3c_hsmmc_readl(HM_PRNSTS) & 0x1))
			break;
	}
	if (i == 0x1000000) {
		printf("@@@@@@1 rHM_PRNSTS: %08lx\r\n", s3c_hsmmc_readl(HM_PRNSTS));
	}

	/* Check CommandInhibit_DAT */
	if (flags & MMC_RSP_BUSY) {
		for (i=0; i<0x1000000; i++) {
			if (!(s3c_hsmmc_readl(HM_PRNSTS) & 0x2))
				break;
		}
		if (i == 0x1000000) {
			printf("@@@@@@2 rHM_PRNSTS: %08lx\r\n", s3c_hsmmc_readl(HM_PRNSTS));
		}
	}

	s3c_hsmmc_writel(arg, HM_ARGUMENT);

	set_cmd_register(cmd, data, flags);

	if (wait_for_cmd_done())
		return 0;

	ClearCommandCompleteStatus();

	if (!(s3c_hsmmc_readw(HM_NORINTSTS) & 0x8000)) {
		return 1;
	} else {
		if (ocr_check == 1)
			return 0;
		else {
			printf("Command = %d, Error Stat = 0x%04x\r\n", (s3c_hsmmc_readw(HM_CMDREG) >> 8), s3c_hsmmc_readw(HM_ERRINTSTS));
			return 0;
		}
	}

}

static void set_mmc_speed (uint eSDSpeedMode)
{
	uint ucSpeedMode, arg;

	ucSpeedMode = (eSDSpeedMode == HIGH) ? 1 : 0;
	arg = (3 << 24) | (185 << 16) | (ucSpeedMode << 8);	/*  Change to the high-speed mode */
	while (!issue_command(MMC_SWITCH, arg, 0, MMC_RSP_R1B));
}

static void set_sd_speed (uint eSDSpeedMode)
{
	uint temp;
	uint arg = 0;
	uchar ucSpeedMode;
	int i;

	ucSpeedMode = (eSDSpeedMode == HIGH) ? 1 : 0;

	if (!issue_command(MMC_SET_BLOCKLEN, 64, 0, MMC_RSP_R1)) {
		printf("CMD16 fail\r\n");
	} else {
		set_blksize_register(7, 64);
		set_blkcnt_register(1);
		set_arg_register(0 * 64);

		set_transfer_mode_register(0, 1, 0, 0, 0);

		arg = (0x1 << 31) | (0xffff << 8) | (ucSpeedMode << 0);

		if (!issue_command(MMC_SWITCH, arg, 0, MMC_RSP_R1B))
			printf("CMD6 fail\r\n");
		else {
			wait_for_r_buf_ready();
			ClearBufferReadReadyStatus();

			for (i = 0; i < 16; i++) {
				temp = s3c_hsmmc_readl(HM_BDATA);
			}

			if (!wait_for_data_done())
				printf(("Transfer NOT Complete\r\n"));

			ClearTransferCompleteStatus();
		}
	}
}

static int get_sd_scr (void)
{
	uint uSCR1, uSCR2;

	if (!issue_command(MMC_SET_BLOCKLEN, 8, 0, MMC_RSP_R1))
		return 0;
	else {
		set_blksize_register(7, 8);
		set_blkcnt_register(1);
		set_arg_register(0 * 8);

		set_transfer_mode_register(0, 1, 0, 0, 0);
		if (!issue_command(MMC_APP_CMD, rca<<16, 0, MMC_RSP_R1))
			return 0;
		else {
			if (!issue_command(SD_APP_SEND_SCR, 0, 1, MMC_RSP_R1))
				return 0;
			else {
				wait_for_r_buf_ready();
				ClearBufferReadReadyStatus();

				uSCR1 = s3c_hsmmc_readl(HM_BDATA);
				uSCR2 = s3c_hsmmc_readl(HM_BDATA);

				if (!wait_for_data_done())
					printf(("Transfer NOT Complete\r\n"));

				ClearTransferCompleteStatus();

				if (uSCR1 & 0x1)
					sd_spec = 1;	/*  Version 1.10, support cmd6 */
				else
					sd_spec = 0;	/*  Version 1.0 ~ 1.01 */

				dbg("sd_spec = %d(0x%08x)\r\n", sd_spec, uSCR1);
				return 1;
			}
		}
	}
}

static int check_card_status(void)
{
	if (!issue_command(MMC_SEND_STATUS, rca<<16, 0, MMC_RSP_R1)) {
		return 0;
	} else {
		if (((s3c_hsmmc_readl(HM_RSPREG0) >> 9) & 0xf) == 4) {
			dbg("Card is transfer status\r\n");
			return 1;
		}
	}

	return 1;
}

static void set_hostctl_speed (uchar mode)
{
	u8 reg8;

	reg8 = s3c_hsmmc_readb(HM_HOSTCTL) & ~(0x1<<2);
	s3c_hsmmc_writeb(reg8 | (mode<<2), HM_HOSTCTL);
}

/* return 0: OK
 * return -1: error
 */
static int set_bus_width (uint width)
{
	uint arg = 0;
	uchar reg = s3c_hsmmc_readb(HM_HOSTCTL);
	uchar bitmode = 0;

	dbg("bus width: %d\r\n", width);

	switch (width) {
	case 8:
		width = mmc_card ? 8 : 4;
		break;
	case 4:
	case 1:
		break;
	default:
		return -1;
	}

	card_irq_enable(0);	// Disable sd card interrupt

	if (mmc_card) { /* MMC Card */
		/* MMC Spec 4.x or above */
		if (mmc_spec == 4) {
			if (width == 1) bitmode = 0;
			else if (width == 4) bitmode = 1;
			else if (width == 8) bitmode = 2;
			else {
				printf("#### unknown mode\r\n");
				return -1;
			}

			arg = ((3 << 24) | (183 << 16) | (bitmode << 8));
			while (!issue_command(MMC_SWITCH, arg, 0, MMC_RSP_R1B));
		} else
			bitmode = 0;
	} else { /* SD Card */
		if (!issue_command(MMC_APP_CMD, rca<<16, 0, MMC_RSP_R1))
			return -1;
		else {
			if (width == 1) {	// 1-bits
				bitmode = 0;
				if (!issue_command(MMC_SWITCH, 0, 0, MMC_RSP_R1B))
					return -1;
			} else {	// 4-bits
				bitmode = 1;
				if (!issue_command(MMC_SWITCH, 2, 0, MMC_RSP_R1B))
					return -1;
			}
		}
	}

	if (bitmode == 2)
		reg |= 1 << 5;
	else
		reg |= bitmode << 1;

	s3c_hsmmc_writeb(reg, HM_HOSTCTL);
	card_irq_enable(1);
	dbg(" transfer rHM_HOSTCTL(0x28) = 0x%02x\r\n", s3c_hsmmc_readb(HM_HOSTCTL));

	return 0;
}

static int set_sd_ocr (void)
{
	uint i, ocr;

	issue_command(MMC_APP_CMD, 0x0, 0, MMC_RSP_R1);
	issue_command(SD_APP_OP_COND, 0x0, 0, MMC_RSP_R3);
	ocr = s3c_hsmmc_readl(HM_RSPREG0);
	dbg("ocr1: %08x\r\n", ocr);

	for (i = 0; i < 100; i++) {
		issue_command(MMC_APP_CMD, 0x0, 0, MMC_RSP_R1);
		issue_command(SD_APP_OP_COND, ocr, 0, MMC_RSP_R3);

		ocr = s3c_hsmmc_readl(HM_RSPREG0);
		dbg("ocr2: %08x\r\n", ocr);
		if (ocr & (0x1 << 31)) {
			dbg("Voltage range: ");
			if (ocr & (1 << 21))
				dbg("2.7V ~ 3.4V");
			else if (ocr & (1 << 20))
				dbg("2.7V ~ 3.3V");
			else if (ocr & (1 << 19))
				dbg("2.7V ~ 3.2V");
			else if (ocr & (1 << 18))
				dbg("2.7V ~ 3.1V");

			if (ocr & (1 << 7))
				dbg(", 1.65V ~ 1.95V\r\n");
			else
				dbg('\r\n');

			mmc_card = 0;
			return 1;
		}
		//udelay(1000);
	}

	// The current card is MMC card, then there's time out error, need to be cleared.
	ClearErrInterruptStatus();
	return 0;
}

static int set_mmc_ocr (void)
{
	uint i, ocr;

	for (i = 0; i < 250; i++) {
		issue_command(MMC_SEND_OP_COND, 0x40FF8000, 0, MMC_RSP_R3);

		ocr = s3c_hsmmc_readl(HM_RSPREG0);
		dbg("ocr1: %08x\r\n", ocr);

		if (ocr & (0x1 << 31)) {
			dbg("Voltage range: ");
			if (ocr & (1 << 21))
				dbg("2.7V ~ 3.4V");
			else if (ocr & (1 << 20))
				dbg("2.7V ~ 3.3V");
			else if (ocr & (1 << 19))
				dbg("2.7V ~ 3.2V");
			else if (ocr & (1 << 18))
				dbg("2.7V ~ 3.1V");
			mmc_card = 1;
			if (ocr & (1 << 7))
				dbg(", 1.65V ~ 1.95V\r\n");
			else
				dbg('\r\n');
			return 1;
		}
	}

	// The current card is SD card, then there's time out error, need to be cleared.
	ClearErrInterruptStatus();
	return 0;
}

#ifdef CONFIG_SUPPORT_MMC_PLUS

static void clock_config (uint clksrc, uint div)
{
	uint base_freq = 0, freq;

	if (clksrc == SD_HCLK)
		base_freq = HCLK;
	else if (clksrc == SD_EPLL)	/* Epll Out 48MHz */
		base_freq = 100000000;
	else
		clksrc = HCLK;

	freq = base_freq / (div * 2);
	dbg("freq = %dMHz\r\n", freq / (1000000));

	if (mmc_card) {
		if (mmc_spec == 4) {
			/* It is necessary to enable the high speed mode
			 * in the card before changing the clock freq
			 * to a freq higher than 20MHz.
			 */
			if (freq > 20000000) {
				set_mmc_speed(HIGH);
				dbg("Set MMC High speed mode OK!!\r\n");
			} else {
				set_mmc_speed(NORMAL);
				dbg("Set MMC Normal speed mode OK!!\r\n");
			}
		} else		// old version
			dbg("Old version MMC card can not support working frequency higher than 20MHz\r\n");
	} else {
		get_sd_scr();
		if (sd_spec == 1) {
			if (freq > 25000000) {
				set_sd_speed(HIGH);	//Higher than 25MHz, should use high speed mode. Max 50MHz and 25MB/sec
				dbg("Set SD High speed mode OK!!\r\n");
			} else {
				set_sd_speed(NORMAL);
				dbg("Set SD Normal speed mode OK!!\r\n");
			}
		} else
			dbg("Old version SD card can not support working frequency higher than 25MHz\r\n");
	}

	/* When Higher than 25MHz, it is necessary
	 * to enable high speed mode of the host controller.
	 */
	if (freq > 25000000) {
		set_hostctl_speed(HIGH);
	} else {
		set_hostctl_speed(NORMAL);
	}

	/* when change the sd clock frequency, need to stop sd clock. */
	hsmmc_clock_onoff(1);
	set_clock(clksrc, div);
	dbg("clock config rHM_HOSTCTL(0x28) = 0x%02x\r\n", s3c_hsmmc_readb(HM_HOSTCTL));

}
#else

static void clock_config (uint clksrc, uint Divisior)
{
	uint SrcFreq, WorkingFreq;

	if (clksrc == SD_HCLK)
		base_freq = HCLK;
	else if (clksrc == SD_EPLL)	/* Epll Out 48MHz */
		base_freq = 100000000;
	else
		clksrc = HCLK;

	WorkingFreq = SrcFreq / (Divisior * 2);
	printf("Card Working Frequency = %dMHz\r\n", WorkingFreq / (1000000));

	if (mmc_card) {
		if (mmc_spec == 4) {
			if (WorkingFreq > 20000000) {	// It is necessary to enable the high speed mode in the card before changing the clock freq to a freq higher than 20MHz.
				set_mmc_speed(HIGH);
				printf("\r\nSet MMC High speed mode OK!!\r\n");
			} else {
				set_mmc_speed(NORMAL);
				printf("\r\nSet MMC Normal speed mode OK!!\r\n");
			}
		} else		// old version
			printf("Old version MMC card can not support working frequency higher than 25MHz");
	}

	if (WorkingFreq > 25000000) {	// Higher than 25MHz, it is necessary to enable high speed mode of the host controller.
		set_hostctl_speed(HIGH);
	} else {
		set_hostctl_speed(NORMAL);
	}

	hsmmc_clock_onoff(0);		// when change the sd clock frequency, need to stop sd clock.
	set_clock(clksrc, Divisior);
	printf("clock config rHM_HOSTCTL = 0x%02x\r\n", s3c_hsmmc_readb(HM_HOSTCTL));

}
#endif

static void check_dma_int (void)
{
	uint i;

	for (i = 0; i < 0x1000000; i++) {
		if (s3c_hsmmc_readw(HM_NORINTSTS) & 0x0002) {
		dbg("Transfer Complete\r\n");
		HS_DMA_END = 1;
		s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | 0x0002, HM_NORINTSTS);
		break;
		}
		if (s3c_hsmmc_readw(HM_NORINTSTS) & 0x8000) {
			printf("error found: %04x\r\n", s3c_hsmmc_readw(HM_ERRINTSTS));
			break;
		}
	}
}


static void display_card_info (void)
{
	uint card_size;

	if (movi_hc) {
		//card_size = process_ext_csd();
	} else {
		uint i, resp[4];
		uint c_size, c_size_multi, read_bl_len, read_bl_partial, blk_size;

		for (i=0; i<4; i++) {
			resp[i] = s3c_hsmmc_readl(HM_RSPREG0+i*4);
			dbg("%08x\r\n", resp[i]);
		}

		read_bl_len = ((resp[2] >> 8) & 0xf);
		read_bl_partial = ((resp[2] >> 7) & 0x1);
		c_size = ((resp[2] & 0x3) << 10) | ((resp[1] >> 22) & 0x3ff);
		c_size_multi = ((resp[1] >> 7) & 0x7);

		card_size = (1 << read_bl_len) * (c_size + 1) * (1 << (c_size_multi + 2)) / 1048576;
		blk_size = (1 << read_bl_len);

		dbg(" read_bl_len: %d\r\n", read_bl_len);
		dbg(" read_bl_partial: %d\r\n", read_bl_partial);
		dbg(" c_size: %d\r\n", c_size);
		dbg(" c_size_multi: %d\r\n", c_size_multi);

		dbg(" One Block Size: %dByte\r\n", blk_size);
		dbg(" Total Card Size: %dMByte\r\n\r\n", card_size + 1);
	}

	printf("%d MB ", card_size + 1);

	if (card_mid == 0x15)
		printf("(MoviNAND)");

	printf("\r\n");
}

static void DataRead_ForCompare (int StartAddr)
{
	uint i = 0, j = 0;
	COMPARE_INT_DONE = 0;

	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSIGEN) & ~(0xffff), HM_NORINTSIGEN);

	Compare_buffer_HSMMC = (uint *) SDI_Compare_buffer_HSMMC;
	for (i = 0; i < (512 * BlockNum_HSMMC) / 4; i++)
		*(Compare_buffer_HSMMC + i) = 0x0;

	dbg("Polling mode data read1\r\n");
	dbg("Read BlockNum = %d\r\n", BlockNum_HSMMC);

	while (!check_card_status());

	/*  Maximum DMA Buffer Size, Block Size */
	set_blksize_register(7, 512);
	/*  Block Numbers to Write */
	set_blkcnt_register(BlockNum_HSMMC);

	if (movi_hc)
		set_arg_register(StartAddr);
	else
		set_arg_register(StartAddr * 512);

	if (BlockNum_HSMMC == 1) {
		dbg("Single block read\r\n");
		set_transfer_mode_register(0, 1, 1, 1, 0);
		/* MMC_READ_SINGLE_BLOCK */
		set_cmd_register(17, 1, MMC_RSP_R1);
	} else {
		dbg("Multi block read\r\n");
		set_transfer_mode_register(1, 1, 1, 1, 0);
		/* MMC_READ_MULTIPLE_BLOCK */
		set_cmd_register(18, 1, MMC_RSP_R1);
	}

	if (wait_for_cmd_done()) {
		printf("Command is NOT completed1\r\n");
	}
	ClearCommandCompleteStatus();

	for (j = 0; j < BlockNum_HSMMC; j++) {
		if (!wait_for_r_buf_ready())
			printf("ReadBuffer NOT Ready\r\n");
		else
			ClearBufferReadReadyStatus();
		for (i = 0; i < 512 / 4; i++) {
			*Compare_buffer_HSMMC++ = s3c_hsmmc_readl(HM_BDATA);
			CompareCnt_INT++;
		}
	}

	dbg("Read count=0x%08x\r\n", CompareCnt_INT);
	if (!wait_for_data_done()) {
		printf(("Transfer NOT Complete\r\n"));
	}
	ClearTransferCompleteStatus();

	dbg("\r\n\r\nHM_NORINTSTS = %x", s3c_hsmmc_readw(HM_NORINTSTS));
}

static void DataCompare_HSMMC (uint a0, uint a1, uint bytes)
{
	uint *pD0 = (uint *) a0;
	uint *pD1 = (uint *) a1;
	uint ErrCnt = 0;
	uint i;

	for (i = 0; i < bytes; i++) {
		if (*pD0 != *pD1) {
			dbg("\r\n%08x=%02x <-> %08x=%02x", pD0, *pD0, pD1, *pD1);
			ErrCnt++;
		}
		pD0++;
		pD1++;
	}
	dbg("\r\nTotal Error cnt = %d", ErrCnt);

	if (ErrCnt == 0)
		dbg("\r\nData Compare Ok\r\n");
}

int hsmmc_init (void)
{
	u32 reg;
	uint width;

	width = 4;

	HCLK = getHCLKClock();
	dbg("HCLK = %08lu\r\n", HCLK);

	hsmmc_clock_onoff(0);

//	reg = readl(SCLK_GATE);
//	writel(reg | (1<<27), SCLK_GATE);

	set_clock(SD_HCLK, 0x80);
	s3c_hsmmc_writeb(0xe, HM_TIMEOUTCON);
	set_hostctl_speed(NORMAL);

	InterruptEnable(0xff, 0xff);

	dbg("HM_NORINTSTS = %x\r\n", s3c_hsmmc_readw(HM_NORINTSTS));

	/* MMC_GO_IDLE_STATE */
	
    issue_command(52, 0x00000c00, 0, 0x00000195);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(52, 0x80000c08, 0, 0x00000195);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(0, 0x00000000, 0, 0x000000c0);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(8, 0x000001aa, 0, 0x000002f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(5, 0x00000000, 0, 0x000002e1);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));
#if 0

    issue_command(55, 0x00000000, 0, 0x000000f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(41, 0x00000000, 0, 0x000000e1);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(0, 0x00000000, 0, 0x000000c0);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(8, 0x00000000, 0, 0x000002f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0x00000000, 0, 0x000000f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(41, 0x00000000, 0, 0x000000e1);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(0, 0x00000000, 0, 0x000000c0);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(8, 0x000001aa , 0, 0x000002f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0x00000000 , 0, 0x000000f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(41, 0x40300000 , 0, 0x000000e1);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0x00000000  , 0, 0x000000f5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(41, 0x40300000 , 0, 0x000000e1);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(2, 0x00000000  , 0, 0x00000067);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(3, 0x00000000  , 0, 0x00000075);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(9, 0xaaaa0000   , 0, 0x00000007);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(7, 0x00000000  , 0, 0x00000015);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0x00000000  , 0, 0x00000095);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(51, 0x00000000  , 0, 0x000000b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0xaaaa0000   , 0, 0x00000095);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(13, 0x00000000  , 0, 0x000001b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


    issue_command(6, 0x00fffff1   , 0, 0x000000b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(6, 0x00fff1ff   , 0, 0x000000b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(6, 0x00ff1fff   , 0, 0x000000b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(55, 0xaaaa0000   , 0, 0x00000095);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(6, 0x00000002   , 0, 0x00000015);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(18, 0x00000000   , 0, 0x000000b5);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));

    issue_command(12, 0x00000000   , 0, 0x0000049d);
    printf("%x %x %x %x\r\n", s3c_hsmmc_readl(HM_RSPREG0)
    , s3c_hsmmc_readl(HM_RSPREG1)
    , s3c_hsmmc_readl(HM_RSPREG2)
    , s3c_hsmmc_readl(HM_RSPREG3));


#endif
	ocr_check = 1;

	if (set_sd_ocr()) {
		mmc_card = 0;
		dbg("SD card is detected\r\n");
	} 
#if 0	
	else if (set_mmc_ocr()) {
		mmc_card = 1;
		dbg("MMC card is detected\r\n");
	}
#endif
	
	else {
		printf("0 MB\r\n");
		return 0;
	}

	ocr_check = 0;

	/* Check the attached card and place the card
	 * in the IDENT state rHM_RSPREG0
	 */
	issue_command(MMC_ALL_SEND_CID, 0, 0, MMC_RSP_R2);

	/* Manufacturer ID */
	card_mid = (s3c_hsmmc_readl(HM_RSPREG3) >> 16) & 0xFF;

	dbg("Product Name : %c%c%c%c%c%c\r\n", ((s3c_hsmmc_readl(HM_RSPREG2) >> 24) & 0xFF),
	       ((s3c_hsmmc_readl(HM_RSPREG2) >> 16) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG2) >> 8) & 0xFF), (s3c_hsmmc_readl(HM_RSPREG2) & 0xFF),
	       ((s3c_hsmmc_readl(HM_RSPREG1) >> 24) & 0xFF), ((s3c_hsmmc_readl(HM_RSPREG1) >> 16) & 0xFF));

	// Send RCA(Relative Card Address). It places the card in the STBY state
	rca = (mmc_card) ? 0x0001 : 0x0000;
	issue_command(MMC_SET_RELATIVE_ADDR, rca<<16, 0, MMC_RSP_R1);

	if (!mmc_card) {
		rca = (s3c_hsmmc_readl(HM_RSPREG0) >> 16) & 0xFFFF;
		printf("=>  rca=0x%08x\r\n", rca);
	}

	dbg("Enter to the Stand-by State\r\n");

	issue_command(MMC_SEND_CSD, rca<<16, 0, MMC_RSP_R2);

	if (mmc_card) {
		mmc_spec = (s3c_hsmmc_readl(HM_RSPREG3) >> 18) & 0xF;
		dbg("mmc_spec=%d\r\n", mmc_spec);
	}

	issue_command(MMC_SELECT_CARD, rca<<16, 0, MMC_RSP_R1);
	dbg("Enter to the Transfer State\r\n");

	display_card_info();

	/* Operating Clock setting */
	clock_config(SD_EPLL, 2);	// Divisor 1 = Base clk /2      ,Divisor 2 = Base clk /4, Divisor 4 = Base clk /8 ...

	while (set_bus_width(width));
	while (!check_card_status());

	/* MMC_SET_BLOCKLEN */
	while (!issue_command(MMC_SET_BLOCKLEN, 512, 0, MMC_RSP_R1));

	s3c_hsmmc_writew(0xffff, HM_NORINTSTS);

	return 0;
}

void movi_write (uint addr, uint start_blk, uint blknum)
{
	uint blksize; //j,

	wt_cnt_HSMMC = 0;
	BlockNum_HSMMC = 0;
	wt_cnt_HSMMC = 0;
	WriteBlockCnt_INT = 0;
	HS_DMA_END = 0;

	BlockNum_HSMMC = blknum;
	blksize = Card_OneBlockSize_ver1;

	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSTSEN) & ~(0xffff)) |
		BUFFER_READREADY_STS_INT_EN |
		BUFFER_WRITEREADY_STS_INT_EN |
		TRANSFERCOMPLETE_STS_INT_EN | COMMANDCOMPLETE_STS_INT_EN, HM_NORINTSTSEN);

	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSIGEN) & ~(0xffff)) | TRANSFERCOMPLETE_SIG_INT_EN, HM_NORINTSIGEN);

	SetSystemAddressReg(addr);		// AHB System Address For Write
	set_blksize_register(7, 512);		// Maximum DMA Buffer Size, Block Size
	set_blkcnt_register(BlockNum_HSMMC);	// Block Numbers to Write

	if (movi_hc)
		set_arg_register(start_blk);
	else
		set_arg_register(start_blk * 512);

	if (BlockNum_HSMMC == 1) {
		set_transfer_mode_register(0, 0, 1, 1, 1);
		set_cmd_register(24, 1, MMC_RSP_R1);
	} else {
		set_transfer_mode_register(1, 0, 1, 1, 1);
		set_cmd_register(25, 1, MMC_RSP_R1);
	}

	if (wait_for_cmd_done()) {
		printf("\r\nCommand is NOT completed3\r\n");
	}
	ClearCommandCompleteStatus();

	/* wait for DMA transfer */
	check_dma_int();
	while (!HS_DMA_END);

	if (!wait_for_data_done()) {
		printf(("Transfer is NOT Complete\r\n"));
	}
	ClearTransferCompleteStatus();

	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 3), HM_NORINTSTS);

//	DataRead_ForCompare(start_blk);
//	DataCompare_HSMMC(uTxBufAddr, uCompareBufAddr, BlockNum_HSMMC * 128);

	BlockNum_HSMMC = 0;
	wt_cnt_HSMMC = 0;
	WriteBlockCnt_INT = 0;
	HS_DMA_END = 0;
	BufferBoundary_INT_Cnt = 0;

	CompareCnt_INT = 0;
	Compare_buffer_HSMMC = 0;
}

void movi_read (uint addr, uint start_blk, uint blknum)
{
	uint blksize; //j, , Addr_temp = start_blk;
	uint dma = 0, cmd, multi; //, TotalReadByte, read_blk_cnt = 0;

	rd_cnt_HSMMC = 0;
	HS_DMA_END = 0;
	BlockNum_HSMMC = 0;
	rd_cnt_HSMMC = 0;
	ReadBlockCnt_INT = 0;

	BlockNum_HSMMC = blknum;

	blksize = Card_OneBlockSize_ver1;

	while (!check_card_status());

	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTSEN) & ~(DMA_STS_INT_EN | BLOCKGAP_EVENT_STS_INT_EN), HM_NORINTSTSEN);
	s3c_hsmmc_writew((HM_NORINTSIGEN & ~(0xffff)) | TRANSFERCOMPLETE_SIG_INT_EN, HM_NORINTSIGEN);

	SetSystemAddressReg(addr);		// AHB System Address For Write
	dma = 1;

	set_blksize_register(7, 512);		// Maximum DMA Buffer Size, Block Size
	set_blkcnt_register(BlockNum_HSMMC);	// Block Numbers to Write

	if (movi_hc)
		set_arg_register(start_blk);		// Card Start Block Address to Write
	else
		set_arg_register(start_blk * 512);	// Card Start Block Address to Write

	cmd = (blknum > 1) ? 18 : 17;
	multi = (blknum > 1);

	set_transfer_mode_register(multi, 1, multi, 1, dma);
	set_cmd_register(cmd, 1, MMC_RSP_R1);

	if (wait_for_cmd_done()) {
		printf(("Command NOT Complete\r\n"));
	} else
		ClearCommandCompleteStatus();


	check_dma_int();
	while (!HS_DMA_END);
	dbg(("\r\nDMA Read End\r\n"));

	HS_DMA_END = 0;
	BlockNum_HSMMC = 0;
	rd_cnt_HSMMC = 0;
	ReadBlockCnt_INT = 0;
}

static void write_test (uint test, uint start_blk, uint blknum)
{
	uint i, blksize; //j,
	uint uTxBufAddr = SDI_Tx_buffer_HSMMC;
	uint uCompareBufAddr = SDI_Compare_buffer_HSMMC;

	wt_cnt_HSMMC = 0;
	BlockNum_HSMMC = 0;
	wt_cnt_HSMMC = 0;
	WriteBlockCnt_INT = 0;
	HS_DMA_END = 0;

	printf("\r\nHS-MMC block Write test: %d, 0x%x 0x%x\r\n", test, start_blk, blknum);

	BlockNum_HSMMC = blknum;

	blksize = Card_OneBlockSize_ver1;

	Tx_buffer_HSMMC = (uint *) SDI_Tx_buffer_HSMMC;
	for (i = 0; i < (blksize * BlockNum_HSMMC) / 4; i++) {
		*(Tx_buffer_HSMMC + i) = ((start_blk<<16) + i);
	}

	printf("## using DMA\r\n");
	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSTSEN) & ~(0xffff)) |
		BUFFER_READREADY_STS_INT_EN |
		BUFFER_WRITEREADY_STS_INT_EN |
		TRANSFERCOMPLETE_STS_INT_EN | COMMANDCOMPLETE_STS_INT_EN, HM_NORINTSTSEN);

	s3c_hsmmc_writew((s3c_hsmmc_readw(HM_NORINTSIGEN) & ~(0xffff)) | TRANSFERCOMPLETE_SIG_INT_EN, HM_NORINTSIGEN);

	SetSystemAddressReg(SDI_Tx_buffer_HSMMC);	// AHB System Address For Write
	set_blksize_register(7, 512);	// Maximum DMA Buffer Size, Block Size
	set_blkcnt_register(BlockNum_HSMMC);	// Block Numbers to Write

	if (movi_hc)
		set_arg_register(start_blk);		// Card Start Block Address to Write
	else
		set_arg_register(start_blk * 512);	// Card Start Block Address to Write

	if (BlockNum_HSMMC == 1) {
		set_transfer_mode_register(0, 0, 1, 1, 1);
		set_cmd_register(24, 1, MMC_RSP_R1);
	} else {
		set_transfer_mode_register(1, 0, 1, 1, 1);
		set_cmd_register(25, 1, MMC_RSP_R1);
	}

	if (wait_for_cmd_done()) {
		printf("\r\nCommand is NOT completed3\r\n");
	}
	ClearCommandCompleteStatus();

	/* wait for DMA transfer */
	check_dma_int();
	while (!HS_DMA_END);

	if (!wait_for_data_done()) {
		printf(("Transfer is NOT Complete\r\n"));
	}
	ClearTransferCompleteStatus();

	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTS) | (1 << 3), HM_NORINTSTS);

	printf(("\r\nDMA Write End\r\n"));

	DataRead_ForCompare(start_blk);
	DataCompare_HSMMC(uTxBufAddr, uCompareBufAddr, BlockNum_HSMMC * 128);

	BlockNum_HSMMC = 0;
	wt_cnt_HSMMC = 0;
	WriteBlockCnt_INT = 0;
	HS_DMA_END = 0;
	BufferBoundary_INT_Cnt = 0;

	CompareCnt_INT = 0;
	Compare_buffer_HSMMC = 0;
}

static void read_test (uint test, uint start_blk, uint blknum)
{
	uint i, blksize; //j, , Addr_temp = start_blk;
	uint dma = 0, cmd, multi; //, TotalReadByte, read_blk_cnt = 0;

	rd_cnt_HSMMC = 0;
	HS_DMA_END = 0;
	BlockNum_HSMMC = 0;
	rd_cnt_HSMMC = 0;
	ReadBlockCnt_INT = 0;

	printf("\r\nHS-MMC block Read test: %d, 0x%x 0x%x\r\n", test, start_blk, blknum);

	BlockNum_HSMMC = blknum;

	blksize = Card_OneBlockSize_ver1;

	Rx_buffer_HSMMC = (uint *) SDI_Rx_buffer_HSMMC;
	for (i = 0; i < (blksize * BlockNum_HSMMC) / 4; i++)
		*(Rx_buffer_HSMMC + i) = 0x0;

	while (!check_card_status());

	s3c_hsmmc_writew(s3c_hsmmc_readw(HM_NORINTSTSEN) & ~(DMA_STS_INT_EN | BLOCKGAP_EVENT_STS_INT_EN), HM_NORINTSTSEN);
	s3c_hsmmc_writew((HM_NORINTSIGEN & ~(0xffff)) | TRANSFERCOMPLETE_SIG_INT_EN, HM_NORINTSIGEN);

	SetSystemAddressReg(SDI_Rx_buffer_HSMMC);	// AHB System Address For Write
	dma = 1;

	set_blksize_register(7, 512);	// Maximum DMA Buffer Size, Block Size
	set_blkcnt_register(BlockNum_HSMMC);	// Block Numbers to Write

	if (movi_hc)
		set_arg_register(start_blk);		// Card Start Block Address to Write
	else
		set_arg_register(start_blk * 512);	// Card Start Block Address to Write

	cmd = (blknum > 1) ? 18 : 17;
	multi = (blknum > 1);

	set_transfer_mode_register(multi, 1, multi, 1, dma);
	set_cmd_register(cmd, 1, MMC_RSP_R1);

	if (wait_for_cmd_done()) {
		printf(("Command NOT Complete\r\n"));
	} else
		ClearCommandCompleteStatus();


	check_dma_int();
	while (!HS_DMA_END);
	printf(("\r\nDMA Read End\r\n"));

	HS_DMA_END = 0;
	BlockNum_HSMMC = 0;
	rd_cnt_HSMMC = 0;
	ReadBlockCnt_INT = 0;
}

void test_hsmmc (uint width, uint test, uint start_blk, uint blknum)
{
	hsmmc_set_gpio();

#if 0
	HS_MMC_CardDetect();
#endif

	hsmmc_reset();

	if (hsmmc_init()) {
		printf("\r\nCard Initialization FAILED\r\n");
	}

}

void testInternal()
{
	hsmmc_set_gpio();

#if 0
	HS_MMC_CardDetect();
#endif

	hsmmc_reset();

	if (hsmmc_init()) {
		printf("\r\nCard Initialization FAILED\r\n");
	}

}

